﻿#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;

use anyhow;
use apiexts::CustomResourceDefinition;
use either::Either::{Left, Right};
use futures::{StreamExt, TryStreamExt};
use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec, DeploymentStatus};
use k8s_openapi::api::core::v1::Service;
use k8s_openapi::api::core::v1::{Node, Pod};
use k8s_openapi::api::extensions::v1beta1::{Ingress, IngressRule};
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1beta1 as apiexts;
use k8s_openapi::apimachinery::pkg::api::resource::Quantity;
use kube::client::ConfigExt;
use kube::{
    self,
    api::{Api, DeleteParams, ListParams, Patch, PatchParams, PostParams, ResourceExt, WatchEvent},
    core::crd::v1beta1::CustomResourceExt,
    Client, Config, CustomResource,
};
use tracing;
use tracing_subscriber;

extern crate hyper;
extern crate hyper_tls;
extern crate tower;
use serde::{Deserialize, Serialize};
use std::io::{Read, Write};
use std::net;
use std::net::Ipv4Addr;
use std::result::Result::Ok;
use std::sync::mpsc::channel;
use std::thread::spawn;
use std::time::Duration;

#[macro_use]
extern crate simple_excel_writer;
use excel::*;
use simple_excel_writer as excel;

//k8s  client封装
mod k8s_client;

use std::collections::HashMap;
use std::process::Command;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    std::env::set_var("RUST_LOG", "info,kube=debug");
    tracing_subscriber::fmt::init();

    //k8s客户端
    let client = k8s_client::init_k8s_client().await?;

    let mut wb = Workbook::create("pods.xlsx");
    let mut sheet_pods = wb.create_sheet("pods");
    let mut sheet_nodes = wb.create_sheet("nodes");

    let mut pod_data: Vec<Vec<String>> = Vec::new();

    //let namespace = "kube-system".to_owned();
    //测试查询pods列表
    //let pods: Api<Pod> = Api::namespaced(client, &namespace);

    let lp = ListParams::default()
        //.fields(&format!("labels.k8s-app={}", "metrics-server"))
        .timeout(10);



    //查询节点的cpu，mem可用配额
    let node_list: Api<Node> = Api::all(client.clone());

    let mut node_data: Vec<Vec<String>> = Vec::new();
    for no in node_list.list(&lp).await? {
        let mut allocatable = no.status.unwrap().allocatable.unwrap();

        let cpu = allocatable.get("cpu").unwrap();
        let memory = allocatable.get("memory").unwrap();
        let mut node_cpu = String::from("");
        let mut node_mem = String::from("");

        if cpu.0.ends_with("m") {
            node_cpu = (cpu.0.replace("m", "").parse::<f64>().unwrap() / 1000.00).to_string();
        } else if cpu.0.ends_with("M") {
            node_cpu = (cpu.0.replace("M", "").parse::<f64>().unwrap() / 1000.00).to_string();
        } else {
            node_cpu = cpu.0.clone();
        }

        if memory.0.ends_with("Gi") {
            node_mem = (memory.0.replace("Gi", "").parse::<f64>().unwrap() * 1000.00).to_string();
        }
        if memory.0.ends_with("G") {
            node_mem = (memory.0.replace("G", "").parse::<f64>().unwrap() * 1000.00).to_string();
        } else if memory.0.ends_with("Mi") {
            node_mem = memory.0.replace("Mi", "");
        } else if memory.0.ends_with("M") {
            node_mem = memory.0.replace("M", "");
        } else if memory.0.ends_with("m") {
            node_mem = memory.0.replace("m", "");
        } else if memory.0.ends_with("Ki") {
            node_mem = (memory.0.replace("Ki", "").parse::<f64>().unwrap() / 1000.00).to_string();
        }

        let v = vec![no.metadata.name.unwrap(), node_cpu, node_mem];
        node_data.push(v);
    }

    //map方式记录节点的cpu，mem可用配额
    let mut node_map_limits_cpu: HashMap<String, f64> = HashMap::new();
    let mut node_map_limits_mem: HashMap<String, f64> = HashMap::new();
    let mut node_map_requests_cpu: HashMap<String, f64> = HashMap::new();
    let mut node_map_requests_mem: HashMap<String, f64> = HashMap::new();

    //节点指标
    let nodes_metrics_map =k8s_client::get_nodes_metrics();

    let pod_list: Api<Pod> = Api::all(client.clone());
    for p in pod_list.list(&lp).await? {
        let mut ns = p.namespace().unwrap().clone();
        let mut pod_name = p.name().clone();
        let containers = p.spec.unwrap().containers;
        let mut pod_host_ip = p.status.unwrap().host_ip.unwrap_or("".to_owned());

        for c in containers {
            let container_name = c.name;
            let resources = c.resources;
            let mut limits_cpu = String::from("");
            let mut limits_memory = String::from("");
            let mut requests_cpu = String::from("");
            let mut requests_memory = String::from("");
            match resources {
                Some(r) => {
                    let limits = r.limits;
                    let requests = r.requests;
                    match limits {
                        Some(l) => {
                            match l.get("cpu") {
                                Some(cpu) => {
                                    if cpu.0.ends_with("m") {
                                        limits_cpu =
                                            (cpu.0.replace("m", "").parse::<f64>().unwrap()
                                                / 1000.00)
                                                .to_string();
                                    } else if cpu.0.ends_with("M") {
                                        limits_cpu =
                                            (cpu.0.replace("M", "").parse::<f64>().unwrap()
                                                / 1000.00)
                                                .to_string();
                                    } else {
                                        limits_cpu = cpu.0.clone();
                                    }
                                }
                                None => {}
                            }
                            match l.get("memory") {
                                Some(memory) => {
                                    limits_memory = memory.0.clone();

                                    if memory.0.ends_with("Gi") {
                                        limits_memory =
                                            (memory.0.replace("Gi", "").parse::<f64>().unwrap()
                                                * 1000.00)
                                                .to_string();
                                    }
                                    if memory.0.ends_with("G") {
                                        limits_memory =
                                            (memory.0.replace("G", "").parse::<f64>().unwrap()
                                                * 1000.00)
                                                .to_string();
                                    } else if memory.0.ends_with("Mi") {
                                        limits_memory = memory.0.replace("Mi", "");
                                    } else if memory.0.ends_with("M") {
                                        limits_memory = memory.0.replace("M", "");
                                    } else if memory.0.ends_with("m") {
                                        limits_memory = memory.0.replace("m", "");
                                    }
                                }
                                None => {}
                            }
                        }
                        None => {}
                    }
                    match requests {
                        Some(l) => {
                            match l.get("cpu") {
                                Some(cpu) => {
                                    if cpu.0.ends_with("m") {
                                        requests_cpu =
                                            (cpu.0.replace("m", "").parse::<f64>().unwrap()
                                                / 1000.00)
                                                .to_string();
                                    } else if cpu.0.ends_with("M") {
                                        requests_cpu =
                                            (cpu.0.replace("M", "").parse::<f64>().unwrap()
                                                / 1000.00)
                                                .to_string();
                                    } else {
                                        requests_cpu = cpu.0.clone();
                                    }
                                }
                                None => {}
                            }
                            match l.get("memory") {
                                Some(memory) => {
                                    if memory.0.ends_with("Gi") {
                                        requests_memory =
                                            (memory.0.replace("Gi", "").parse::<f64>().unwrap()
                                                * 1000.00)
                                                .to_string();
                                    }
                                    if memory.0.ends_with("G") {
                                        requests_memory =
                                            (memory.0.replace("G", "").parse::<f64>().unwrap()
                                                * 1000.00)
                                                .to_string();
                                    } else if memory.0.ends_with("Mi") {
                                        requests_memory = memory.0.replace("Mi", "");
                                    } else if memory.0.ends_with("M") {
                                        requests_memory = memory.0.replace("M", "");
                                    } else if memory.0.ends_with("m") {
                                        requests_memory = memory.0.replace("m", "");
                                    }
                                }
                                None => {}
                            }
                        }
                        None => {}
                    }
                }
                None => {}
            }
            tracing::info!(
                "Namespace:{} ,Found pod: {},Found Container: {},limits:cpu:{},memory:{},requests:cpu:{},memory:{}",
                &ns,
                &pod_name,
                &container_name,
                &limits_cpu,
                &limits_memory,
                &requests_cpu,
                &requests_memory
            );
            if (pod_host_ip != "") {
                if (node_map_requests_mem.contains_key(&pod_host_ip)) {
                    let old = node_map_requests_mem.get(&pod_host_ip).unwrap();
                    if (requests_memory != "") {
                        node_map_requests_mem.insert(
                            pod_host_ip.to_owned(),
                            old + (requests_memory.parse::<f64>().unwrap()),
                        );
                    }
                } else {
                    if (requests_memory != "") {
                        node_map_requests_mem.insert(
                            pod_host_ip.to_owned(),
                            requests_memory.parse::<f64>().unwrap(),
                        );
                    }
                }
                if (node_map_limits_mem.contains_key(&pod_host_ip)) {
                    let old = node_map_limits_mem.get(&pod_host_ip).unwrap();
                    if (limits_memory != "") {
                        node_map_limits_mem.insert(
                            pod_host_ip.to_owned(),
                            old + (limits_memory.parse::<f64>().unwrap()),
                        );
                    }
                } else {
                    if (limits_memory != "") {
                        node_map_limits_mem.insert(
                            pod_host_ip.to_owned(),
                            limits_memory.parse::<f64>().unwrap(),
                        );
                    }
                }

                if (node_map_requests_cpu.contains_key(&pod_host_ip)) {
                    let old = node_map_requests_cpu.get(&pod_host_ip).unwrap();
                    if (requests_cpu != "") {
                        node_map_requests_cpu.insert(
                            pod_host_ip.to_owned(),
                            old + (requests_cpu.parse::<f64>().unwrap()),
                        );
                    }
                } else {
                    if (requests_cpu != "") {
                        node_map_requests_cpu
                            .insert(pod_host_ip.to_owned(), requests_cpu.parse::<f64>().unwrap());
                    }
                }
                if (node_map_limits_cpu.contains_key(&pod_host_ip)) {
                    let old = node_map_limits_cpu.get(&pod_host_ip).unwrap();
                    if (limits_cpu != "") {
                        node_map_limits_cpu.insert(
                            pod_host_ip.to_owned(),
                            old + (limits_cpu.parse::<f64>().unwrap()),
                        );
                    }
                } else {
                    if (limits_cpu != "") {
                        node_map_limits_cpu
                            .insert(pod_host_ip.to_owned(), limits_cpu.parse::<f64>().unwrap());
                    }
                }
            }
            let v = vec![
                ns.clone(),
                pod_name.clone(),
                container_name.clone(),
                limits_cpu.clone(),
                limits_memory.clone(),
                requests_cpu.clone(),
                requests_memory.clone(),
                pod_host_ip.clone(),
            ];
            pod_data.push(v);
        }
    }

    sheet_pods.add_column(Column { width: 20.0 });
    sheet_pods.add_column(Column { width: 40.0 });
    sheet_pods.add_column(Column { width: 20.0 });
    sheet_pods.add_column(Column { width: 20.0 });
    sheet_pods.add_column(Column { width: 20.0 });
    sheet_pods.add_column(Column { width: 20.0 });
    sheet_pods.add_column(Column { width: 20.0 });
    sheet_pods.add_column(Column { width: 20.0 });
    //写pods列表
    wb.write_sheet(&mut sheet_pods, |sheet_writer| {
        let sw = sheet_writer;
        sw.append_row(row![
            "namespace",
            "pod",
            "container",
            "limits.cpu",
            "limits.mem(M)",
            "requests.cpu",
            "requests.mem(M)",
            "host_ip"
        ])
        .expect("write excel error!");
        for d in pod_data {
            sw.append_row(row![
                d[0].clone(),
                d[1].clone(),
                d[2].clone(),
                d[3].parse::<f64>().unwrap_or(0.0),
                d[4].parse::<f64>().unwrap_or(0.0),
                d[5].parse::<f64>().unwrap_or(0.0),
                d[6].parse::<f64>().unwrap_or(0.0),
                d[7].clone()
            ])
            .unwrap();
        }
        Ok(())
    })
    .expect("write excel error!");

    // set column width
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    sheet_nodes.add_column(Column { width: 20.0 });
    //写node列表
    wb.write_sheet(&mut sheet_nodes, |sheet_writer| {
        let sw = sheet_writer;
        sw.append_row(row![
            "host_ip",
            "limits.cpu",
            "limits.mem(M)",
            "requests.cpu",
            "requests.mem(M)",
            "node.cpu",
            "node.mem(M)",
            "cpu申请占用率(%)",
            "内存申请占用率(%)",
            "cpu实际使用",
            "内存实际使用（M）"
        ])
        .expect("write excel error!");
        print!("{:?}", &node_map_limits_cpu);
        for no in node_data {
            let h = no[0].clone();
            sw.append_row(row![
                h.clone(),
                node_map_limits_cpu.get(&h).unwrap().clone(),
                node_map_limits_mem.get(&h).unwrap().clone(),
                node_map_requests_cpu.get(&h).unwrap().clone(),
                node_map_requests_mem.get(&h).unwrap().clone(),
                no[1].clone().parse::<f64>().unwrap_or(0.0),
                no[2].clone().parse::<f64>().unwrap_or(0.0),
                node_map_requests_cpu.get(&h).unwrap().clone()
                    / no[1].clone().parse::<f64>().unwrap_or(0.0)
                    * 100.0,
                node_map_requests_mem.get(&h).unwrap().clone()
                    / no[2].clone().parse::<f64>().unwrap_or(0.0)
                    * 100.0,
                *nodes_metrics_map.get(&format!("{}_cpu",&h)).unwrap_or(&0.0),
                *nodes_metrics_map.get(&format!("{}_memory",&h)).unwrap_or(&0.0)
            ])
            .unwrap();
        }
        Ok(())
    })
    .expect("write excel error!");

    wb.close().expect("close excel error!");

    Ok(())
}
